<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */
declare (strict_types=1);

namespace lflow\impl;

use lflow\core\Execution;
use lflow\lib\interface\WorkFlowInterceptor;
use lflow\scheduling\IScheduler;

/**
 * 时限控制拦截器
 * 主要拦截任务的expireDate(期望完成时间)
 * 再交给具体的调度器完成调度处理
 *
 * @author Mr.April
 * @since  1.0
 */
class SchedulerInterceptor implements WorkFlowInterceptor
{

    /**
     * 调度器接口
     */
    private ?IScheduler $scheduler;

    /**
     * 是否调度
     */
    private bool $isScheduled = true;

    public function intercept(Execution $execution): void
    {
        if (!$this->isScheduled) return;
        foreach ($execution->getTasks() as $key => $task) {
            $id         = $execution->getProcess()->getId() . "/" . $execution->getOrder()->getId() . "/" . $task->getId();
            $expireDate = (int)$task->getExpireDate();
            if (!empty($expireDate)) {
                $this->schedule($id, $task, $expireDate, 0, $execution->getArgs());
            }
            $task->setRemindDate('1 day');
            $remindDate = (int)$task->getRemindDate();
            if (!empty($remindDate)) {
                $this->schedule($id, $task, $remindDate, 1, $execution->getArgs());
            }
        }
    }

    public function schedule(string $id, Task $task, int $startDate, int $jobType, string $args)
    {
        try {
            $entity = new JobEntity($id, $task, $startDate, $args);
            $entity->setModelName($task->getTaskName());
            $entity->setJobType($jobType);
            if ($jobType == JobType::EXECUTER()->ordinal()) {
                //自动运行
                $model = $task->getModel();
                if (!empty($model) && is_numeric($model->getExpireTime()) && $model->getAutoExecute() == '1') {
                    $entity->setPeriod((int)$model->getExpireTime());
                    $this->scheduleDispatch($entity);
                }
            }
            if ($jobType == JobType::REMINDER()->ordinal()) {
                //消息提醒
                $model = $task->getModel();
                if (!empty($model) && is_numeric($model->getReminderRepeat())) {
                    $entity->setPeriod((int)$model->getReminderRepeat());
                    $this->scheduleDispatch($entity);
                }
            }
        } catch (\Exception $e) {
            Logger::error($e->getMessage());
            Logger:: info("scheduler failed.task is:" . json_encode($task));
        }
    }

    private function scheduleDispatch(JobEntity $entity): void
    {
        new Scheduling();
        $classes = get_declared_classes();
        foreach ($classes as $klass) {
            $reflect = new ReflectionClass($klass);
            if ($reflect->implementsInterface(IScheduler::class)) {
                try {
                    $schedule = new ProxyHelper($klass);
                    $schedule->schedule($entity);
                } catch (WorkFlowException $e) {
                    $this->isScheduled = false;
                    Logger::error("\nReminderJob execute taskId:{}\n" . json_encode($e->getMessage()));
                }
            }
        }
    }
}
